import pyglet
import ctypes
import win32api
import win32gui
import win32con
import random

from pyglet.gl import (
    glActiveTexture,
    glBindTexture,
    glEnable,
    glBlendFunc,
    glDisable,
    GL_TEXTURE0,
    GL_TRIANGLES,
    GL_BLEND,
    GL_SRC_ALPHA,
    GL_ONE_MINUS_SRC_ALPHA,
)
from pyglet.graphics import Group
from pyglet.graphics.shader import Shader, ShaderProgram

ctypes.windll.shcore.SetProcessDpiAwareness(2)

_vertex_source = """#version 330 core
    in vec2 position;
    in vec3 tex_coords;
    out vec3 texture_coords;

    uniform WindowBlock 
    {                       // This UBO is defined on Window creation, and available
        mat4 projection;    // in all Shaders. You can modify these matrixes with the
        mat4 view;          // Window.view and Window.projection properties.
    } window;  

    void main()
    {
        gl_Position = window.projection * window.view * vec4(position, 1, 1);
        texture_coords = tex_coords;
    }
"""

_fragment_source = """#version 330 core
    in vec3 texture_coords;
    out vec4 final_colors;

    uniform sampler2D our_texture;

    void main()
    {
        final_colors = texture(our_texture, texture_coords.xy);
    }
"""

_fragment_source = """#version 330 core
    in vec3 texture_coords;
    out vec4 final_colors;

    uniform sampler2D our_texture;

    void main()
    {
        vec4 color = texture(our_texture, texture_coords.xy);
        // final_colors = vec4(1.0 - color.rgb, color.a);
        final_colors = vec4(0.0, 0.0, 0.0, color.a);
    }
"""

vert_shader = Shader(_vertex_source, 'vertex')
frag_shader = Shader(_fragment_source, 'fragment')
shader_program = ShaderProgram(vert_shader, frag_shader)


class RenderGroup(Group):
    """A Group that enables and binds a Texture and ShaderProgram.

    RenderGroups are equal if their Texture and ShaderProgram
    are equal.
    """
    def __init__(self, texture, program, order=0, parent=None):
        """Create a RenderGroup.

        :Parameters:
            `texture` : `~pyglet.image.Texture`
                Texture to bind.
            `program` : `~pyglet.graphics.shader.ShaderProgram`
                ShaderProgram to use.
            `order` : int
                Change the order to render above or below other Groups.
            `parent` : `~pyglet.graphics.Group`
                Parent group.
        """
        super().__init__(order, parent)
        self.texture = texture
        self.program = program

    def set_state(self):
        glActiveTexture(GL_TEXTURE0)
        glBindTexture(self.texture.target, self.texture.id)
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        self.program.use()

    def unset_state(self):
        glDisable(GL_BLEND)

    def __hash__(self):
        return hash((self.texture.target, self.texture.id, self.order, self.parent, self.program))

    def __eq__(self, other):
        return (self.__class__ is other.__class__ and
                self.texture.target == other.texture.target and
                self.texture.id == other.texture.id and
                self.order == other.order and
                self.program == other.program and
                self.parent == other.parent)

def create_quad(x, y, texture):
    x2 = x + texture.width
    y2 = y + texture.height
    return x, y, x2, y, x2, y2, x, y2


def set_window_through(hwnd, through=True):
    ex_style = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
    if through:
        new_style = ex_style | win32con.WS_EX_TRANSPARENT
    else:
        new_style = ex_style & ~win32con.WS_EX_TRANSPARENT

    win32gui.SetWindowLong(
        hwnd,
        win32con.GWL_EXSTYLE,
        new_style | win32con.WS_EX_LAYERED
    )

def set_window_borderless(hwnd):
    win32gui.SetWindowLong(
        hwnd, 
        win32con.GWL_STYLE,
        win32con.WS_VISIBLE | win32con.WS_POPUP
    )

def set_window_topmost(hwnd, topmost=True):
    win32gui.SetWindowPos(
        hwnd,
        win32con.HWND_TOPMOST if topmost else win32con.HWND_NOTOPMOST,
        0, 0, 0, 0,
        win32con.SWP_NOMOVE | win32con.SWP_NOSIZE | win32con.SWP_FRAMECHANGED
    )


class Surface:
    def __init__(self, window, hwnd, x, y):
        self.window = window
        self.hwnd = hwnd
        self.x = x
        self.y = y

    def relocation(self, *args):
        self.window.set_location(self.x, self.y)

monitors = win32api.EnumDisplayMonitors()
screens = pyglet.display.get_display().get_screens()
surfaces = []
for monitor, screen in zip(monitors, screens):
    info = win32api.GetMonitorInfo(monitor[0])
    width = info['Monitor'][2] - info['Monitor'][0]
    height = info['Monitor'][3] - info['Monitor'][1]
    
    window = pyglet.window.Window(
        width=width,
        height=height,
        screen=screen,
        style=pyglet.window.Window.WINDOW_STYLE_TRANSPARENT,
        config=pyglet.gl.Config(
            alpha_size=8,
            double_buffer=True,
            depth_size=0,
            samples=4
        ),
        visible=False
    )
    hwnd = window._hwnd

    wr = win32gui.GetWindowRect(hwnd)
    cr = win32gui.GetClientRect(hwnd)
    th = (wr[3] - wr[1]) - (cr[3] - cr[1])

    o = th // 2
    x = info['Monitor'][0]
    y = info['Monitor'][1] + o - (o - 18)

    window.set_location(x - width, y)

    set_window_through(hwnd)
    set_window_topmost(hwnd)

    surface = Surface(window, hwnd, x, y)
    surfaces.append(surface)


batch = pyglet.graphics.Batch()

ciallo = pyglet.image.load_animation("ciallo.gif")
# ciallo = pyglet.image.load("img.png")
sprite = pyglet.sprite.Sprite(ciallo)
sprite.x = (window.width - sprite.width) // 2
sprite.y = (window.height - sprite.height) // 2

for frame in ciallo.frames:
    tex = frame.image.get_texture()

group = RenderGroup(tex, shader_program)
indices = (0, 1, 2, 0, 2, 3)
vertex_positions = create_quad(576, 296, tex)
vertex_list = shader_program.vertex_list_indexed(4, GL_TRIANGLES, indices, batch, group,
                                                 position=('f', vertex_positions),
                                                 tex_coords=('f', tex.tex_coords))

for surface in surfaces:
    window = surface.window
    hwnd = surface.hwnd

    label = pyglet.text.Label(
        "Ciallo～(∠・ω< )⌒★", 
        font_size=24,
        x=window.width,
        y=window.height // 2,
        anchor_x='left',
        anchor_y='center',
        batch=batch
    )
    label.color = (255, 255, 255, 255)
    move_speed = 150

    def update(dt):
        label.x -= move_speed * dt

        label.color = (
            random.randint(0, 255),
            random.randint(0, 255),
            random.randint(0, 255),
            255
        )
        
        if label.x + label.content_width < 0:
            label.x = window.width

    @window.event
    def on_draw():
        window.clear()
        # pyglet.gl.glClearColor(0.5, 0, 0, 0.5)
        batch.draw()

    window.set_visible(True)
    set_window_borderless(hwnd)
    pyglet.clock.schedule_once(surface.relocation, 0.05)
    pyglet.clock.schedule_interval(update, 1/60.0)

pyglet.app.run()